/*
 * main.c
 *
 *  Created on: 24/apr/2013
 *      Authors: Ialenti Andrea, Marangon Fabio
 */

#include "../headers/Graph.h"
#include "../headers/Heuristic.h"
#include "../headers/Lagrangian.h"
#include "../headers/OneTree.h"
#include "../headers/Printer.h"
#include "../headers/Utility.h"
#include "../headers/StatusCodes.h"

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//	This function is used to calculate the edge costs for the different type of TSP-LIB formats.
Status setWeights(const Config *cfg, Graph *g, const TSPLib type);

Status genRandGraph(Graph *g, int size, int seed) {
	//TODO
	return SUCCESS;
}

Status importGraph(const Config *cfg, const char *fileName, Graph *g, bool nodePerm, bool verbose) {
	char *funcName = initCharArray(cfg->NAME_LEN);
	sprintf(funcName, "importGraph");

	FILE *file;
	char *line = initCharArray(cfg->MAX_LINE_LEN);
	char *token;
	int nodes;
	bool data = false;
	int nodeIndex;
	double x, y;
	TSPLib dataType = EUC_2D;

	//	Open the file in read text mode.
	if ((file = fopen (fileName, "rt")) == NULL) {
		fprintf(stdout, "***'%s': cannot read TSP-lib input file '%s'!\n", funcName, fileName);
		freeCharArray(funcName);
		freeCharArray(line);
		return ERR_READ_FILE;
	}

	//	Read file, line by line.
	while (fgets (line, cfg->MAX_LINE_LEN, file) != NULL && strcmp(line, "EOF\n") != 0) {

		//	Check the start of data in file.
		if (!data && strcmp(line, "NODE_COORD_SECTION\n") == 0) {
			data = true;
			continue;
		}

		//	Tokenize each line.
		token = strtok(line, " : ");

		//	Acquire graph size and initialize the memory.
		if (strcmp(token, "DIMENSION") == 0) {
			token = strtok(NULL, " : ");
			nodes = atoi(token);
			initGraph(cfg, g, nodes, 0);
		}

		//	Acquire data type.
		else if (strcmp(token, "EDGE_WEIGHT_TYPE") == 0) {
			token = strtok(NULL, " : ");

			if (strcmp(token, "EUC_2D\n") == 0)
				dataType = EUC_2D;
			else if (strcmp(token, "GEO\n") == 0)
				dataType = GEO;
			else if (strcmp(token, "CEIL_2D\n") == 0)
				dataType = CEIL_2D;
		}

		//	Read nodes coordinates.
		if (data && strcmp(line, "EOF\n") != 0) {
			nodeIndex = atoi(token) - 1;
			token = strtok(NULL, " ");
			x = atof(token);
			token = strtok(NULL, " ");
			y = atof(token);

			g->nodes[nodeIndex].id = nodeIndex;
			g->nodes[nodeIndex].x = x;
			g->nodes[nodeIndex].y = y;
			g->nodes[nodeIndex].degree = 0;
			g->nodes[nodeIndex].forced = 0;
		}
	}
	fclose(file);

	//	Perform the graph nodes random permutation.
	if (nodePerm)
		permNodes(cfg, g, verbose);

	//	Calculate the edges costs using the appropriate distance (create the whole matrix).
	setWeights(cfg, g, dataType);

	//	Store the graph name.
	char *ptr = strrchr(fileName, '/');
	strcpy(g->name, ptr + 1);

	freeCharArray(funcName);
	freeCharArray(line);
	return SUCCESS;
}

Status restoreOrigCosts(Graph *g) {
    int i, j;

    for (i = 0; i < g->size; i++)
		for (j = 0; j < g->size; j++)
			g->edges[i][j].cost = g->costs_origin[i][j];
    return SUCCESS;
}

Status rmEdge(Graph *g, int i, int j) {

	//	Decrease the nodes degree and set the edge cost to zero.
	g->nodes[i].degree--;
	g->nodes[j].degree--;
	g->costs_origin[j][i] = g->costs_origin[i][j] = 0;
	g->edges[j][i].cost = g->edges[i][j].cost = 0;

	return SUCCESS;
}

Status setWeights(const Config *cfg, Graph *g, const TSPLib type) {
	int i, j;

	//	Calculate edge weight as euclidean distances.
	if (type == EUC_2D)
		for (i = 0; i < g->size; i++)
			for (j = 0; j < g->size; j++) {
				g->edges[i][j].start = &g->nodes[i];
				g->edges[i][j].end = &g->nodes[j];
				g->edges[i][j].cost = (i != j) ? round((sqrt(pow((g->nodes[i].x - g->nodes[j].x), 2.0) + pow((g->nodes[i].y - g->nodes[j].y), 2.0)))) : 0;
				g->costs_origin[i][j] = (i != j) ? g->edges[i][j].cost : 0;
				if(g->edges[i][j].cost > g->max)
					g->max = g->edges[i][j].cost;

				//	Update nodes degree.
				if (g->edges[i][j].cost != 0 && i > j) {
					g->nodes[i].degree++;
					g->nodes[j].degree++;
				}
			}

	//	Calculate edge weight from geographical coordinates.
	else if (type == GEO) {
		int *degree = initIntArray(4);
		double *minute = initDblArray(4);
		double *latitude = initDblArray(2);
		double *longitude = initDblArray(2);
		for (i = 0; i < g->size; i++)
			for (j = 0; j < g->size; j++) {
				if(i != j) {

					//	Calculate longitude and latitude.
					degree[0] = (int)g->nodes[i].x;
					degree[1] = (int)g->nodes[i].y;
					degree[2] = (int)g->nodes[j].x;
					degree[3] = (int)g->nodes[j].y;
					minute[0] = g->nodes[i].x - degree[0];
					minute[1] = g->nodes[i].y - degree[1];
					minute[2] = g->nodes[j].x - degree[2];
					minute[3] = g->nodes[j].y - degree[3];
					latitude[0] = cfg->PI * (degree[0] + 5.0 * minute[0] / 3.0) / 180.0;
					longitude[0] = cfg->PI * (degree[1] + 5.0 * minute[1] / 3.0) / 180.0;
					latitude[1] = cfg->PI * (degree[2] + 5.0 * minute[2] / 3.0) / 180.0;
					longitude[1] = cfg->PI * (degree[3] + 5.0 * minute[3] / 3.0) / 180.0;

					//	Calculate geographical distances.
					double q1 = cos(longitude[0] - longitude[1]);
					double q2 = cos(latitude[0] - latitude[1]);
					double q3 = cos(latitude[0] + latitude[1]);

					g->edges[i][j].start = &g->nodes[i];
					g->edges[i][j].end = &g->nodes[j];
					g->edges[i][j].cost = g->costs_origin[i][j] = (int)(cfg->RADIUS * acos(0.5 * ((1.0 + q1) * q2 - (1.0 - q1) * q3)) + 1.0);
					if(g->edges[i][j].cost > g->max)
						g->max = g->edges[i][j].cost;
				}
				else
					g->edges[i][j].cost = g->costs_origin[i][j] = 0;

				//	Update nodes degree.
				if (g->edges[i][j].cost != 0 && i > j) {
					g->nodes[i].degree++;
					g->nodes[j].degree++;
				}
			}

		//	Free memory.
		freeIntArray(degree);
		freeDblArray(minute);
		freeDblArray(latitude);
		freeDblArray(longitude);
	}

	//	Calculate edge weight as Manhattan distances.
	else if (type == CEIL_2D)
		for (i = 0; i < g->size; i++)
			for (j = 0; j < g->size; j++) {
				//TODO

				//	Update nodes degree.
				if (g->edges[i][j].cost != 0 && i > j) {
					g->nodes[i].degree++;
					g->nodes[j].degree++;
				}
			}

	//	Calculate edge weight for explicit data.
	else if (type == EXPLICIT)
		for (i = 0; i < g->size; i++)
			for (j = 0; j < g->size; j++) {
				//TODO

				//	Update nodes degree.
				if (g->edges[i][j].cost != 0 && i > j) {
					g->nodes[i].degree++;
					g->nodes[j].degree++;
				}
			}
	return SUCCESS;
}

Status permNodes(const Config *cfg, Graph *g, bool verbose) {
	char *funcName = initCharArray(cfg->NAME_LEN);
	sprintf(funcName, "permNodes");

	if(verbose)
		fprintf(stdout, "\t\t'%s': randomly permuting graph nodes. . .\n", funcName);

	int i, r;
	for (i = 0; i < g->size; i++) {

		//	Swap node 'i' with node 'r', do not swap the nodes if they are the same one.
		r = rand() % g->size;
		if (r != i) {
			swapDbl(&g->nodes[i].x, &g->nodes[r].x);
			swapDbl(&g->nodes[i].y, &g->nodes[r].y);
		}
	}

	freeCharArray(funcName);
	return SUCCESS;
}

Status cpGraph(const Graph *src, Graph *dest) {
	int i, j;

	//	Copy single value fields.
	dest->edgeCnt = src->edgeCnt;
	dest->max = src->max;
	dest->min = src->min;
	dest->seed = src->seed;
	dest->size = src->size;
	strcpy(dest->name, src->name);

	//	Copy the nodes and the array 'u'.
	for(i = 0; i < dest->size; i++) {
		dest->nodes[i].id = src->nodes[i].id;
		dest->nodes[i].x = src->nodes[i].x;
		dest->nodes[i].y = src->nodes[i].y;
		dest->nodes[i].degree = src->nodes[i].degree;
		dest->nodes[i].forced = src->nodes[i].forced;

		dest->u[i] = src->u[i];
	}

	//	Copy the costs.
	for(i = 0; i < dest->size; i++)
		for(j = 0; j < dest->size; j++) {

			dest->edges[i][j].cost = src->edges[i][j].cost;
			dest->edges[i][j].start = &dest->nodes[i];
			dest->edges[i][j].end = &dest->nodes[j];
			dest->edges[i][j].prev = NULL;//todo
			dest->edges[i][j].next = NULL;//todo

			dest->costs_origin[i][j] = src->costs_origin[i][j];
		}

	return SUCCESS;
}
